package act.app;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import act.Act;
import act.Destroyable;
import act.app.data.BinderManager;
import act.app.data.StringValueResolverManager;
import act.app.event.AppEventId;
import act.app.util.AppCrypto;
import act.app.util.NamedPort;
import act.boot.BootstrapClassLoader;
import act.cli.CliDispatcher;
import act.cli.bytecode.CommanderByteCodeScanner;
import act.conf.AppConfLoader;
import act.conf.AppConfig;
import act.conf.AppConfigKey;
import act.controller.bytecode.ControllerByteCodeScanner;
import act.data.DataPropertyRepository;
import act.data.JodaDateTimeCodec;
import act.data.util.ActPropertyHandlerFactory;
import act.event.AppEventListenerBase;
import act.event.EventBus;
import act.event.bytecode.SimpleEventListenerByteCodeScanner;
import act.handler.RequestHandler;
import act.handler.builtin.StaticResourceGetter;
import act.handler.builtin.controller.FastRequestHandler;
import act.inject.DependencyInjectionBinder;
import act.inject.DependencyInjector;
import act.inject.genie.GenieInjector;
import act.inject.genie.GenieModuleScanner;
import act.inject.param.JsonDTOClassManager;
import act.inject.param.ParamValueLoaderManager;
import act.job.AppJobManager;
import act.job.bytecode.JobByteCodeScanner;
import act.mail.MailerConfigManager;
import act.mail.bytecode.MailerByteCodeScanner;
import act.route.RouteSource;
import act.route.RouteTableRouterBuilder;
import act.route.Router;
import act.util.*;
import act.view.ActErrorResult;
import act.view.ImplicitVariableProvider;
import act.view.rythm.JodaDateTimeFormatter;
import act.view.rythm.JodaTransformers;
import act.view.rythm.RythmTransformerScanner;
import act.view.rythm.RythmView;
import org.osgl.$;
import org.osgl.Osgl;
import org.osgl.cache.CacheService;
import org.osgl.http.H;
import org.osgl.http.HttpConfig;
import org.osgl.logging.LogManager;
import org.osgl.logging.Logger;
import org.osgl.mvc.MvcConfig;
import org.osgl.mvc.result.Result;
import org.osgl.storage.IStorageService;
import org.osgl.util.*;
import org.rythmengine.utils.I18N;
import javax.enterprise.context.ApplicationScoped;
import java.io.File;
import java.io.IOException;
import java.util.*;
import static act.app.event.AppEventId.*;
/**
* {@code App} represents an application that is deployed in a Act container
*/
public class App extends DestroyableBase {
@Deprecated
public static final Logger logger = Act.LOGGER;
public static final Logger LOGGER = Act.LOGGER;
private static App INST;
public enum F {
;
public static $.Predicate<String> JAVA_SOURCE = S.F.endsWith(".java");
public static $.Predicate<String> JAR_FILE = S.F.endsWith(".jar");
public static $.Predicate<String> CONF_FILE = S.F.endsWith(".conf").or(S.F.endsWith(".properties").or(S.F.endsWith(".yaml").or(S.F.endsWith(".yml").or(S.F.endsWith(".xml")))));
public static $.Predicate<String> ROUTES_FILE = $.F.eq(RouteTableRouterBuilder.ROUTES_FILE);
}
private volatile String profile;
private String name;
private String id;
private File appBase;
private File appHome;
private Router router;
private CliDispatcher cliDispatcher;
private Map<NamedPort, Router> moreRouters;
private AppConfig<?> config;
private AppClassLoader classLoader;
private ProjectLayout layout;
private AppBuilder builder;
private EventBus eventBus;
private AppCodeScannerManager scannerManager;
private DbServiceManager dbServiceManager;
private AppJobManager jobManager;
private CliServer cliServer;
private MailerConfigManager mailerConfigManager;
private StringValueResolverManager resolverManager;
private SingletonRegistry singletonRegistry;
private BinderManager binderManager;
private AppInterceptorManager interceptorManager;
private DependencyInjector<?> dependencyInjector;
private IStorageService uploadFileStorageService;
private AppServiceRegistry appServiceRegistry;
private Map<String, Daemon> daemonRegistry;
private AppCrypto crypto;
private IdGenerator idGenerator;
private CacheService cache;
// used in dev mode only
private CompilationException compilationException;
private AppEventId currentState;
private Set<AppEventId> eventEmitted;
private Thread mainThread;
private Set<String> scanList;
private List<File> baseDirs;
private volatile File tmpDir;
private boolean restarting;
private Result blockIssue;
private Exception blockIssueCause;
private RequestHandler blockIssueHandler = new FastRequestHandler() {
@Override
public void handle(ActionContext context) {
E.illegalArgumentIf(null == blockIssue);
blockIssue.apply(context.req(), context.resp());
}
};
protected App() {
INST = this;
}
protected App(File appBase, ProjectLayout layout) {
this("MyApp", appBase, layout);
}
protected App(String name, File appBase, ProjectLayout layout) {
this.name = name;
this.id = generateId(name);
this.appBase = appBase;
this.layout = layout;
this.appHome = RuntimeDirs.home(this);
INST = this;
}
public static App instance() {
return INST;
}
App name(String name) {
this.name = name;
this.id = generateId(name);
return this;
}
public String name() {
return name;
}
/**
* Returns short id which is derived from passed in app name.
*
* **Note** `App.id()` is by no means to create a unique identifier of application.
*
* @return the short name
*/
public String id() {
return id;
}
private static String generateId(String name) {
String id;
if (S.blank(name) || "MyApp".equals(name)) {
return "act";
}
String[] sa = name.split("[\\s]+");
int len = sa.length;
switch (len) {
case 1:
String s = sa[0];
id = s.length() > 2 ? s.substring(0, 3) : s;
break;
case 2:
String s1 = sa[0], s2 = sa[1];
s1 = s1.length() > 1 ? s1.substring(0, 2) : s1;
s2 = s2.length() > 1 ? s2.substring(0, 2) : s2;
id = S.concat(s1, "-", s2);
break;
default:
id = S.concat(
sa[0].substring(0, 1),
sa[1].substring(0, 1),
sa[2].substring(0, 1),
"-"
);
}
return id;
}
public String profile() {
if (null == profile) {
synchronized (this) {
if (null == profile) {
String s = SysProps.get(AppConfigKey.PROFILE.key());
if (null == s) {
s = Act.mode().name().toLowerCase();
}
profile = s;
}
}
}
return profile;
}
public Act.Mode mode() {
return Act.mode();
}
public boolean isDev() {
return mode().isDev();
}
public boolean isProd() {
return mode().isProd();
}
public AppConfig<?> config() {
return config;
}
public List<File> baseDirs() {
if (null == baseDirs) {
baseDirs = C.newList();
if (null != appBase && appBase.isDirectory()) {
baseDirs.add(appBase);
}
for (File baseDir : config.moduleBases()) {
if (null != baseDir && baseDir.isDirectory()) {
baseDirs.add(baseDir);
}
}
}
return baseDirs;
}
public List<File> sourceDirs() {
return layoutDirs(ProjectLayout.F.SRC.curry(layout()));
}
public List<File> resourceDirs() {
return layoutDirs(ProjectLayout.F.RSRC.curry(layout()));
}
public List<File> libDirs() {
return layoutDirs(ProjectLayout.F.LIB.curry(layout()));
}
public List<File> testSourceDirs() {
return layoutDirs(ProjectLayout.F.TST_SRC.curry(layout()));
}
public List<File> testResourceDirs() {
return layoutDirs(ProjectLayout.F.TST_RSRC.curry(layout()));
}
public List<File> testLibDirs() {
return layoutDirs(ProjectLayout.F.TST_LIB.curry(layout()));
}
public List<File> allSourceDirs(boolean requireTestProfile) {
List<File> dirs = C.newList();
dirs.addAll(sourceDirs());
if (!requireTestProfile || "test".equals(Act.profile())) {
dirs.addAll(testSourceDirs());
}
return dirs;
}
public List<File> allResourceDirs(boolean requireTestProfile) {
List<File> dirs = C.newList();
dirs.addAll(resourceDirs());
if (!requireTestProfile || "test".equals(Act.profile())) {
dirs.addAll(testResourceDirs());
}
return dirs;
}
public List<File> allLibDirs(boolean requireTestProfile) {
List<File> dirs = C.newList();
dirs.addAll(libDirs());
if (!requireTestProfile || "test".equals(Act.profile())) {
dirs.addAll(testLibDirs());
}
return dirs;
}
private List<File> layoutDirs($.Function<File, File> transformer) {
return C.list(baseDirs()).map(transformer);
}
public CliDispatcher cliDispatcher() {
return cliDispatcher;
}
public Router router() {
return router;
}
public Router router(String name) {
if (S.blank(name)) {
return router();
}
for (Map.Entry<NamedPort, Router> entry : moreRouters.entrySet()) {
if (S.eq(entry.getKey().name(), name)) {
return entry.getValue();
}
}
return null;
}
public Router router(NamedPort port) {
if (null == port) {
return router();
}
return moreRouters.get(port);
}
public AppCrypto crypto() {
return crypto;
}
/**
* The base dir where an application sit within
*/
public File base() {
return appBase;
}
/**
* The home dir of an application, referenced only
* at runtime.
* <p><b>Note</b> when app is running in dev mode, {@code appHome}
* shall be {@code appBase/target}, while app is deployed to
* Act at other mode, {@code appHome} shall be the same as
* {@code appBase}</p>
*/
public File home() {
return appHome;
}
public AppClassLoader classLoader() {
return classLoader;
}
public ProjectLayout layout() {
return layout;
}
public boolean checkUpdates(boolean async) {
if (!Act.isDev()) {
return false;
}
synchronized (this) {
try {
detectChanges();
return false;
} catch (RequestRefreshClassLoader refreshRequest) {
refresh(async);
return true;
} catch (RequestServerRestart requestServerRestart) {
refresh(async);
return true;
}
}
}
public synchronized void detectChanges() {
if (null == classLoader) {
throw new RequestServerRestart();
}
classLoader.detectChanges();
if (null != compilationException) {
throw ActErrorResult.of(compilationException);
}
}
public void restart() {
build();
refresh();
}
public synchronized void setBlockIssue(Exception e) {
if (e instanceof ActErrorResult) {
blockIssue = (ActErrorResult) e;
} else {
if (null != classLoader()) {
blockIssue = ActErrorResult.of(e);
blockIssueCause = null;
} else {
blockIssueCause = e;
}
}
}
/**
* In dev mode it could request app to refresh. However if
* the request is issued in a thread that will be interrupted
* e.g. the cli thread, it should call refresh in an new thread
*/
public void asyncRefresh() {
new Thread() {
@Override
public void run() {
refresh();
}
}.start();
}
public boolean isStarted() {
return currentState == POST_START || currentState == ACT_START;
}
public boolean isMainThread() {
return Thread.currentThread() == mainThread;
}
public void shutdown() {
Act.shutdownApp(this);
}
@Override
protected void releaseResources() {
mainThread.interrupt();
if (null == daemonRegistry) {
return;
}
LOGGER.info("App shutting down ....");
if (null != classLoader && config().i18nEnabled()) {
// clear resource bundle cache for Act I18n
ResourceBundle.clearCache(classLoader);
// clear resource bundle cache for Rythm I18n
ResourceBundle.clearCache(I18N.class.getClassLoader());
}
for (Daemon d : daemonRegistry.values()) {
stopDaemon(d);
}
shutdownCliServer();
shutdownEventBus();
shutdownJobManager();
clearServiceResourceManager();
classLoader = null;
}
public synchronized void refresh(boolean async) {
if (async) {
asyncRefresh();
} else {
refresh();
}
}
public synchronized boolean isRestarting() {
return restarting;
}
public RequestHandler blockIssueHandler() {
if (null != blockIssue && Act.isDev()) {
return blockIssueHandler;
}
return null;
}
public synchronized void refresh() {
currentState = null;
long ms = $.ms();
LOGGER.info("App starting ....");
profile = null;
blockIssue = null;
blockIssueCause = null;
Act.viewManager().clearAppDefinedVars();
initScanlist();
initServiceResourceManager();
reload();
mainThread = Thread.currentThread();
restarting = mainThread.getName().contains("job");
eventEmitted = C.newSet();
initSingletonRegistry();
initEventBus();
emit(EVENT_BUS_INITIALIZED);
loadConfig();
emit(CONFIG_LOADED);
initCache();
initDataPropertyRepository();
initCrypto();
initIdGenerator();
initJobManager();
initDaemonRegistry();
initInterceptorManager();
initResolverManager();
initBinderManager();
initUploadFileStorageService();
initRouters();
emit(ROUTER_INITIALIZED);
loadRoutes();
emit(ROUTER_LOADED);
initCliDispatcher();
initCliServer();
initDbServiceManager();
emit(DB_SVC_LOADED);
Act.viewManager().reset();
loadGlobalPlugin();
emit(APP_ACT_PLUGIN_LOADED);
initScannerManager();
loadActScanners();
loadBuiltInScanners();
emit(PRE_LOAD_CLASSES);
initClassLoader();
emit(AppEventId.CLASS_LOADER_INITIALIZED);
preloadClasses();
try {
scanAppCodes();
compilationException = null;
} catch (CompilationException e) {
compilationException = e;
throw ActErrorResult.of(e);
}
//classLoader().loadClasses();
emit(APP_CODE_SCANNED);
emit(CLASS_LOADED);
Act.viewManager().reload(this);
loadDependencyInjector();
emit(DEPENDENCY_INJECTOR_LOADED);
initJsonDTOClassManager();
initParamValueLoaderManager();
initMailerConfigManager();
// setting context class loader here might lead to memory leaks
// and cause weird problems as class loader been set to thread
// could be switched to handling other app in ACT or still hold
// old app class loader instance after the app been refreshed
// - Thread.currentThread().setContextClassLoader(classLoader());
initHttpConfig();
initViewManager();
// let's any emit the dependency injector loaded event
// in case some other service depend on this event.
// If any DI plugin e.g. guice has emitted this event
// already, it doesn't matter we emit the event again
// because once app event is consumed the event listeners
// are cleared
emit(DEPENDENCY_INJECTOR_PROVISIONED);
emit(SINGLETON_PROVISIONED);
config().preloadConfigurations();
emit(PRE_START);
emit(START);
daemonKeeper();
if (null != blockIssueCause) {
setBlockIssue(blockIssueCause);
}
LOGGER.info("App[%s] loaded in %sms", name(), $.ms() - ms);
emit(POST_START);
}
public AppBuilder builder() {
return builder;
}
void build() {
builder = AppBuilder.build(this);
}
void hook() {
Act.hook(this);
}
public File tmpDir() {
if (null == tmpDir) {
synchronized (this) {
if (Act.isDev()) {
tmpDir = new File(this.layout().target(appBase), "tmp");
} else {
try {
tmpDir = java.nio.file.Files.createTempDirectory(name()).toFile();
} catch (IOException e) {
throw E.ioException(e);
}
}
}
}
return tmpDir;
}
public File file(String path) {
return new File(base(), path);
}
public File resource(String path) {
return new File(this.layout().resource(appBase), path);
}
public void registerDaemon(Daemon daemon) {
daemonRegistry.put(daemon.id(), daemon);
}
public void unregisterDaemon(Daemon daemon) {
daemonRegistry.remove(daemon.id());
}
List<Daemon> registeredDaemons() {
return C.list(daemonRegistry.values());
}
Daemon registeredDaemon(String id) {
return daemonRegistry.get(id);
}
public <T> void registerSingleton(Class<? extends T> cls, T instance) {
if (null != singletonRegistry) {
singletonRegistry.register(cls, instance);
}
}
public void registerSingletonClass(Class<?> aClass) {
singletonRegistry.register(aClass);
}
public void registerSingleton(Object instance) {
singletonRegistry.register(instance.getClass(), instance);
}
public AppInterceptorManager interceptorManager() {
return interceptorManager;
}
public AppCodeScannerManager scannerManager() {
return scannerManager;
}
public DbServiceManager dbServiceManager() {
return dbServiceManager;
}
public StringValueResolverManager resolverManager() {
return resolverManager;
}
public BinderManager binderManager() {
return binderManager;
}
public CacheService cache() {
return cache;
}
public CacheService cache(String name) {
return config().cacheService(name);
}
public MailerConfigManager mailerConfigManager() {
return mailerConfigManager;
}
public EventBus eventBus() {
return eventBus;
}
public AppJobManager jobManager() {
return jobManager;
}
public <DI extends DependencyInjector> App injector(DI dependencyInjector) {
E.NPE(dependencyInjector);
E.illegalStateIf(null != this.dependencyInjector, "Dependency injection factory already set");
this.dependencyInjector = dependencyInjector;
return this;
}
public <DI extends DependencyInjector> DI injector() {
return (DI) dependencyInjector;
}
public IStorageService uploadFileStorageService() {
return uploadFileStorageService;
}
public String sign(String message) {
return crypto().sign(message);
}
public String encrypt(String message) {
return crypto().encrypt(message);
}
public String decrypt(String message) {
return crypto().decrypt(message);
}
public <T> T singleton(Class<T> clz) {
return singletonRegistry.get(clz);
}
/**
* Get/Create new instance of a class specified by the className
* <p/>
* **Note** if the class is a singleton class, then the singleton instance
* will be returned
*
* @param className the className of the instance to be returned
* @param <T> the generic type of the class
* @return the instance of the class
*/
public <T> T getInstance(String className) {
Class<T> c = $.classForName(className, classLoader());
return getInstance(c);
}
public <T> T getInstance(Class<T> clz) {
if (null == dependencyInjector) {
return $.newInstance(clz);
}
return dependencyInjector.get(clz);
}
@Override
public int hashCode() {
return appBase.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof App) {
App that = (App) obj;
return $.eq(that.appBase, appBase);
}
return false;
}
@Override
public String toString() {
String path = appBase.getPath();
return S.concat("app@[", path, "]");
}
/**
* Return an ID in string that is unique across the cluster
*
* @return
*/
public String cuid() {
return idGenerator.genId();
}
public <T extends AppService<T>> T service(Class<T> serviceClass) {
return appServiceRegistry.lookup(serviceClass);
}
App register(final AppService service) {
return register(service, false);
}
App register(final AppService service, boolean noDiBinder) {
if (null == appServiceRegistry) {
return this; // for unit test only
}
appServiceRegistry.register(service);
if (null != eventBus && !noDiBinder) {
eventBus.bind(AppEventId.DEPENDENCY_INJECTOR_LOADED, new AppEventListenerBase() {
@Override
public void on(EventObject event) throws Exception {
final App app = App.this;
eventBus.emit(new DependencyInjectionBinder(app, service.getClass()) {
@Override
public Object resolve(App app) {
return app.service(service.getClass());
}
});
}
});
}
return this;
}
public void emit(AppEventId appEvent) {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace(S.concat("emitting event: ", appEvent.name()));
}
currentState = appEvent;
eventEmitted().add(appEvent);
EventBus bus = eventBus();
if (null != bus) {
bus.emit(appEvent);
}
}
public Set<String> scanList() {
return new HashSet<String>(scanList);
}
private Set<AppEventId> eventEmitted() {
return eventEmitted;
}
public boolean eventEmitted(AppEventId appEvent) {
return eventEmitted().contains(appEvent);
}
public AppEventId currentState() {
return currentState;
}
private void loadConfig() {
JsonUtilConfig.configure(this);
File resource = RuntimeDirs.resource(this);
LOGGER.debug("loading app configuration: %s ...", appBase.getAbsolutePath());
config = new AppConfLoader().load(resource);
config.app(this);
configureLoggingLevels();
registerSingleton(AppConfig.class, config);
registerValueObjectCodec();
if (config.i18nEnabled()) {
MvcConfig.enableLocalizedErrorMsg();
}
}
private void initHttpConfig() {
HttpConfig.secure(config.httpSecure());
HttpConfig.securePort(config.httpExternalSecurePort());
HttpConfig.nonSecurePort(config.httpExternalPort());
HttpConfig.defaultLocale(config.locale());
HttpConfig.domain(config.host());
}
// TODO: move this to somewhere that is more appropriate
private void registerValueObjectCodec() {
ValueObject.register(new JodaDateTimeCodec(config));
}
private void initIdGenerator() {
idGenerator = new IdGenerator(
config().nodeIdProvider(),
config().startIdProvider(),
config().sequenceProvider(),
config().longEncoder()
);
}
private void initDaemonRegistry() {
if (null != daemonRegistry) {
Destroyable.Util.tryDestroyAll(daemonRegistry.values(), ApplicationScoped.class);
}
daemonRegistry = C.newMap();
jobManager.on(AppEventId.START, new Runnable() {
@Override
public void run() {
jobManager.fixedDelay("daemon-keeper", new Runnable() {
@Override
public void run() {
daemonKeeper();
}
}, "1mn");
}
});
}
private void daemonKeeper() {
final String KEY_COUNTER = "c";
final String KEY_SEQ_NO = "sn";
final String KEY_LAST_SEQ_NO = "lsn";
for (final Daemon d : daemonRegistry.values()) {
Daemon.State state = d.state();
if (state == Daemon.State.STARTED) {
// daemon running successfully, clear the error recovery state
if (d.getAttribute(KEY_COUNTER) != null) {
d.removeAttribute(KEY_COUNTER);
d.removeAttribute(KEY_SEQ_NO);
d.removeAttribute(KEY_LAST_SEQ_NO);
}
} else if (state == Daemon.State.STOPPED) {
startDaemon(d);
} else if (state == Daemon.State.ERROR) {
// try to recover daemon, the recovery duration shall
// follow a fibonacci sequence
Integer counter = d.getAttribute(KEY_COUNTER);
Integer seqNo, lastSeqNo;
if (null == counter) {
counter = 1;
seqNo = 1;
lastSeqNo = 0;
} else {
seqNo = d.getAttribute(KEY_SEQ_NO);
lastSeqNo = d.getAttribute(KEY_LAST_SEQ_NO);
}
if (--counter == 0) {
startDaemon(d);
int nextSeqNo = seqNo + lastSeqNo;
lastSeqNo = seqNo;
seqNo = nextSeqNo;
counter = seqNo;
d.setAttribute(KEY_SEQ_NO, seqNo);
d.setAttribute(KEY_LAST_SEQ_NO, lastSeqNo);
}
d.setAttribute(KEY_COUNTER, counter);
}
}
}
private void startDaemon(final Daemon daemon) {
jobManager().now(new Runnable() {
@Override
public void run() {
try {
daemon.start();
} catch (Exception e) {
logger.error(e, "Error starting daemon [%s]", daemon.id());
}
}
});
}
private void stopDaemon(final Daemon daemon) {
daemon.stop();
}
private void initServiceResourceManager() {
clearServiceResourceManager();
appServiceRegistry = new AppServiceRegistry(this);
}
private void clearServiceResourceManager() {
if (null != appServiceRegistry) {
eventBus().emit(STOP);
appServiceRegistry.destroy();
dependencyInjector = null;
if (null != cache) {
cache.shutdown();
}
}
}
private void initUploadFileStorageService() {
uploadFileStorageService = UploadFileStorageService.create(this);
}
private void initCliDispatcher() {
if (config().cliEnabled()) {
cliDispatcher = new CliDispatcher(this);
}
}
private void initCliServer() {
if (config().cliEnabled()) {
cliServer = new CliServer(this);
}
}
private void shutdownCliServer() {
if (null != cliServer) {
cliServer.destroy();
}
}
private void initRouters() {
router = new Router(this);
moreRouters = C.newMap();
List<NamedPort> ports = config().namedPorts();
for (NamedPort port : ports) {
moreRouters.put(port, new Router(this, port.name()));
}
if (config.cliOverHttp()) {
NamedPort cliOverHttp = new NamedPort(AppConfig.PORT_CLI_OVER_HTTP, config.cliOverHttpPort());
moreRouters.put(cliOverHttp, new Router(this, AppConfig.PORT_CLI_OVER_HTTP));
}
}
private void initEventBus() {
eventBus = new EventBus(this);
}
public void shutdownEventBus() {
if (null != eventBus) {
eventBus.destroy();
}
}
private void initCache() {
cache = cache(config().cacheName());
cache.startup();
CacheService sessionCache = cache(config().cacheNameSession());
if (cache != sessionCache) {
sessionCache.startup();
}
HttpConfig.setSessionCache(sessionCache);
}
private void initCrypto() {
crypto = new AppCrypto(config());
registerSingleton(AppCrypto.class, crypto);
}
private void initJobManager() {
jobManager = new AppJobManager(this);
}
private void shutdownJobManager() {
if (null != jobManager) {
jobManager.destroy();
}
}
private void initScanlist() {
ClassLoader classLoader = getClass().getClassLoader();
if (classLoader instanceof BootstrapClassLoader) {
scanList = ((BootstrapClassLoader) classLoader).scanList();
}
}
private void initInterceptorManager() {
interceptorManager = new AppInterceptorManager(this);
}
private void initScannerManager() {
scannerManager = new AppCodeScannerManager(this);
}
private void initDbServiceManager() {
dbServiceManager = new DbServiceManager(this);
}
private void initDataPropertyRepository() {
new DataPropertyRepository(this);
}
private void initMailerConfigManager() {
mailerConfigManager = new MailerConfigManager(this);
}
private void loadGlobalPlugin() {
Act.appServicePluginManager().applyTo(this);
}
private void loadActScanners() {
Act.scannerPluginManager().initApp(this);
}
private void loadBuiltInScanners() {
scannerManager.register(new GenieModuleScanner());
scannerManager.register(new ClassInfoByteCodeScanner());
scannerManager.register(new ClassFinderByteCodeScanner());
scannerManager.register(new ControllerByteCodeScanner());
scannerManager.register(new MailerByteCodeScanner());
scannerManager.register(new JobByteCodeScanner());
scannerManager.register(new SimpleBean.ByteCodeScanner());
scannerManager.register(new SimpleEventListenerByteCodeScanner());
scannerManager.register(new CommanderByteCodeScanner());
scannerManager.register(new RythmTransformerScanner());
scannerManager.register(new ImplicitVariableProvider.TemplateVariableScanner(this));
}
private void loadDependencyInjector() {
DependencyInjector di = injector();
if (null == di) {
new GenieInjector(this);
} else {
LOGGER.warn("Third party injector[%s] loaded. Please consider using Act air injection instead", di.getClass());
}
}
private void loadRoutes() {
loadBuiltInRoutes();
LOGGER.debug("loading app routing table: %s ...", appBase.getPath());
List<File> routes;
if (Act.isProd()) {
routes = RuntimeDirs.routes(this);
} else {
routes = layout().routeTables(base());
}
for (File route : routes) {
if (route.exists() && route.canRead() && route.isFile()) {
List<String> lines = IO.readLines(route);
new RouteTableRouterBuilder(lines).build(router);
}
}
}
private void loadBuiltInRoutes() {
router().addMapping(H.Method.GET, "/asset/", new StaticResourceGetter("asset"), RouteSource.BUILD_IN);
router().addMapping(H.Method.GET, "/asset/act/", new StaticResourceGetter("asset/act"), RouteSource.BUILD_IN);
if (config().allowDownloadUploadFile()) {
router().addMapping(H.Method.GET, "/~upload/{path}", new UploadFileStorageService.UploadFileGetter(), RouteSource.BUILD_IN);
}
router().addContext("act.", "/~");
if (config.cliOverHttp()) {
Router router = router(AppConfig.PORT_CLI_OVER_HTTP);
router.addMapping(H.Method.GET, "/asset/", new StaticResourceGetter("asset"), RouteSource.BUILD_IN);
}
}
private void initClassLoader() {
classLoader = Act.mode().classLoader(this);
}
private void initJsonDTOClassManager() {
new JsonDTOClassManager(this);
}
private void preloadClasses() {
classLoader.preload();
}
private void initResolverManager() {
resolverManager = new StringValueResolverManager(this);
Osgl.propertyHandlerFactory = new ActPropertyHandlerFactory(this);
}
private void initBinderManager() {
binderManager = new BinderManager(this);
}
private void initParamValueLoaderManager() {
new ParamValueLoaderManager(this);
}
private void initSingletonRegistry() {
singletonRegistry = new SingletonRegistry(this);
singletonRegistry.register(App.class, this);
appServiceRegistry.bulkRegisterSingleton();
}
private void loadPlugins() {
// TODO: load app level plugins
}
private void initViewManager() {
Act.viewManager().onAppStart();
registerBuiltInRythmTransformers();
}
private void registerBuiltInRythmTransformers() {
RythmView rythmView = (RythmView) Act.viewManager().view(RythmView.ID);
rythmView.registerBuiltInTransformer(this, JodaTransformers.class);
rythmView.registerFormatter(this, new JodaDateTimeFormatter());
}
private void configureLoggingLevels() {
Map map = config().subSet("log.level");
map.putAll(config().subSet("act.log.level"));
for (Object o : map.entrySet()) {
Map.Entry<String, String> entry = $.cast(o);
String key = entry.getKey();
if (key.startsWith("log.level")) {
key = key.substring("log.level.".length());
} else {
key = key.substring("act.log.level.".length());
}
Logger.Level level = loggerLevelOf(entry.getValue());
E.invalidConfigurationIf(null == level, "Unknown log level: %s", entry.getValue());
Logger logger = LogManager.get(key);
logger.setLevel(level);
}
}
private Logger.Level loggerLevelOf(String s) {
Map<String, Logger.Level> map = new HashMap<>();
for (Logger.Level level : Logger.Level.values()) {
map.put(level.name().toUpperCase(), level);
}
map.put("WARNING", Logger.Level.WARN);
return map.get(s.toUpperCase());
}
private void scanAppCodes() {
classLoader().scan();
//classLoader().scan();
}
static App create(File appBase, ProjectLayout layout) {
return new App(appBase, layout);
}
}